// ValidationAspects
// Written by Mike Saunders
// Released under Microsoft Public License (Ms-PL)
using System;
using System.Linq;
using System.Reflection;
using NUnit.Framework;
using ValidationAspects;
using ValidationAspects.Sdk.Interception;

namespace TestValidationAspects.Sdk.Interception
{
	[TestFixture]
	public class TestGetInterceptionValidationMethods
	{
		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void Test_ForType_throws_for_null_Type_param()
		{
			GetInterceptionValidationMethods.ForType(null, false).Count();
		}

		[Test]
		public void Test_ForType_throws_for_static_methods_applied_with_object_validators()
		{
			MethodInfo methodInfo = typeof(DecoratedStaticMethod).GetMethod("DoIt");
			Assert.IsNotNull(methodInfo);
			Test.Throws<ValidationException>(() => GetInterceptionValidationMethods.ForType(typeof(DecoratedStaticMethod), false).Count(),
			                                 string.Format("Object Validation cannot be applied to [method:{0}] [type:{1}]. Method is static or abstract.", methodInfo, methodInfo.DeclaringType));
		}

		[Test]
		public void Test_ForType_returns_only_decorated_methods_and_parameters()
		{
			var methods = GetInterceptionValidationMethods.ForType(typeof(Dummy), true);

			Assert.AreEqual(2, methods.Count());
			Assert.IsTrue(methods.Contains(typeof(Dummy).GetMethod("DecoratedMethod")));
			Assert.IsTrue(methods.Contains(typeof(Dummy).GetMethod("DecoratedParameter")));
		}

		[Test]
		public void Test_ForType_throws_for_method_out_parameters_applied_with_validator()
		{
			MethodInfo methodInfo = typeof(DecoratedOutParameter).GetMethod("DoIt");
			Assert.IsNotNull(methodInfo);
			Test.Throws<ValidationException>(() => GetInterceptionValidationMethods.ForType(typeof(DecoratedOutParameter), false).Count(),
			                                 string.Format("Validation cannot be applied to out parameter [name:{0}] [method:{1}] [type:{2}]. Parameter must not declare the out modifier.",
			                                               methodInfo.GetParameters()[0].Name, methodInfo.Name, methodInfo.DeclaringType));
		}

		[Test]
		public void Test_ForType_returns_all_decorated_methods_and_parameters()
		{
			var methods = GetInterceptionValidationMethods.ForType(typeof(Dummy), false);

			Assert.AreEqual(5, methods.Count());
			Assert.IsTrue(methods.Contains(typeof(Dummy).GetMethod("DecoratedMethod")));
			Assert.IsTrue(methods.Contains(typeof(Dummy).GetMethod("NonDecoratedMethod")));
			Assert.IsTrue(methods.Contains(typeof(Dummy).GetMethod("DecoratedParameter")));
			Assert.IsTrue(methods.Contains(typeof(Dummy).GetMethod("NonDecoratedParameter")));
			Assert.IsTrue(methods.Contains(typeof(Dummy).GetMethod("StaticMethodParameter")));
		}

		#region Mocks
		public class Dummy
		{
			[ValidateObject]
			public void DecoratedMethod() {}

			public void NonDecoratedMethod() {}

			public void DecoratedParameter([NotNullOrEmpty] string s, out bool b)
			{
				b = false;
			}

			public void NonDecoratedParameter(string s, out bool b)
			{
				b = false;
			}

			public static void StaticMethod() {}
			public static void StaticMethodParameter(string s) {}

			[ValidationMethod]
			public static void ValidateDummy(Dummy dummy) {}
		}

		public class DecoratedStaticMethod
		{
			[ValidateObject]
			public static void DoIt() {}
		}

		public class DecoratedOutParameter
		{
			public void DoIt([NotNullOrEmpty] out string s)
			{
				s = null;
			}
		}
		#endregion
	}
}